/*
 * Copyright 2008 Zoundry LLC, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package org.zoundry.gaedt.ui.wizards;

import java.io.File;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.ui.wizards.project.IWizardNewProjectNameAndLocationPage;

/**
 * First page for the new Google App Engine project creation wizard. This page
 * collects the name and location of the new project.
 */
public class ZGaeWizardNewProjectNameAndLocationPage extends WizardPage implements
        IWizardNewProjectNameAndLocationPage
{
    // constants
    private static final int SIZING_TEXT_FIELD_WIDTH = 250;

    /** Whether to use default or custom project location */
    private boolean mUseDefaults = true;
    /** initial project field value */
    private String mInitialProjectFieldValue;
    /** initial location field value */
    private IPath mInitialLocationFieldValue;
    /** the value the user has entered */
    private String mCustomLocationFieldValue;

    /** project name field. */
    private Text mProjectNameField;
    /** location path field */
    private Text mLocationPathField;
    /** location label */
    private Label mLocationLabel;
    /** browse button */
    private Button mBrowseButton;

    /** Modify listener for project name changes */
    private Listener mNameModifyListener = new Listener()
    {
        public void handleEvent(Event e)
        {
            setLocationForSelection();
            setPageComplete(validatePage());
        }
    };

    /** Modify listener for project location changes */
    private Listener mLocationModifyListener = new Listener()
    {
        public void handleEvent(Event e)
        {
            setPageComplete(validatePage());
        }
    };

    /**
     * Creates a new project creation wizard page.
     * 
     * @param pageName the name of this page
     */
    public ZGaeWizardNewProjectNameAndLocationPage()
    {
        super("NewGaeProjectPage");
        setTitle("Google App Engine Project");
        setDescription("Create a new Google App Engine Project.");
        setPageComplete(false);
        mInitialLocationFieldValue = Platform.getLocation();
        mCustomLocationFieldValue = ""; //$NON-NLS-1$
    }

    /**
     * @see org.python.pydev.ui.wizards.project.IWizardNewProjectNameAndLocationPage#getProjectType()
     */
    public String getProjectType()
    {
        return IPythonNature.PYTHON_VERSION_2_5;
    }

    /**
     * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
     */
    public void createControl(Composite parent)
    {
        Composite composite = new Composite(parent, SWT.NULL);
        composite.setLayout(new GridLayout());
        composite.setLayoutData(new GridData(GridData.FILL_BOTH));
        composite.setFont(parent.getFont());

        createProjectNameGroup(composite);
        createProjectLocationGroup(composite);

        validatePage();

        // Show description on opening
        setErrorMessage(null);
        setMessage(null);
        setControl(composite);
    }

    /**
     * Creates the project location specification controls.
     * 
     * @param parent the parent composite
     */
    private final void createProjectLocationGroup(Composite parent)
    {
        Font font = parent.getFont();
        // project specification group
        Composite projectGroup = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout();
        layout.numColumns = 3;
        projectGroup.setLayout(layout);
        projectGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        projectGroup.setFont(font);

        // new project label
        Label projectContentsLabel = new Label(projectGroup, SWT.NONE);
        projectContentsLabel.setFont(font);

        projectContentsLabel.setText("Project contents:");

        GridData labelData = new GridData();
        labelData.horizontalSpan = 3;
        projectContentsLabel.setLayoutData(labelData);

        final Button useDefaultsButton = new Button(projectGroup, SWT.CHECK | SWT.RIGHT);
        useDefaultsButton.setText("Use &default");
        useDefaultsButton.setSelection(mUseDefaults);
        useDefaultsButton.setFont(font);

        GridData buttonData = new GridData();
        buttonData.horizontalSpan = 3;
        useDefaultsButton.setLayoutData(buttonData);

        createUserSpecifiedProjectLocationGroup(projectGroup, !mUseDefaults);

        SelectionListener listener = new SelectionAdapter()
        {
            public void widgetSelected(SelectionEvent e)
            {
                mUseDefaults = useDefaultsButton.getSelection();
                mBrowseButton.setEnabled(!mUseDefaults);
                mLocationPathField.setEnabled(!mUseDefaults);
                mLocationLabel.setEnabled(!mUseDefaults);
                if (mUseDefaults)
                {
                    mCustomLocationFieldValue = mLocationPathField.getText();
                    setLocationForSelection();
                }
                else
                {
                    mLocationPathField.setText(mCustomLocationFieldValue);
                }
            }
        };
        useDefaultsButton.addSelectionListener(listener);
    }

    /**
     * Creates the project name specification controls.
     * 
     * @param parent the parent composite
     */
    private final void createProjectNameGroup(Composite parent)
    {
        Font font = parent.getFont();
        // project specification group
        Composite projectGroup = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout();
        layout.numColumns = 2;
        projectGroup.setLayout(layout);
        projectGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        // new project label
        Label projectLabel = new Label(projectGroup, SWT.NONE);
        projectLabel.setFont(font);

        projectLabel.setText("&Project name:");

        // new project name entry field
        mProjectNameField = new Text(projectGroup, SWT.BORDER);
        GridData data = new GridData(GridData.FILL_HORIZONTAL);
        data.widthHint = SIZING_TEXT_FIELD_WIDTH;
        mProjectNameField.setLayoutData(data);
        mProjectNameField.setFont(font);

        // Set the initial value first before listener
        // to avoid handling an event during the creation.
        if (mInitialProjectFieldValue != null)
            mProjectNameField.setText(mInitialProjectFieldValue);
        mProjectNameField.addListener(SWT.Modify, mNameModifyListener);
    }

    /**
     * Creates the project location specification controls.
     * 
     * @param projectGroup the parent composite
     * @param enabled the initial enabled state of the widgets created
     */
    private void createUserSpecifiedProjectLocationGroup(Composite projectGroup, boolean enabled)
    {
        Font font = projectGroup.getFont();
        // location label
        mLocationLabel = new Label(projectGroup, SWT.NONE);
        mLocationLabel.setFont(font);
        mLocationLabel.setText("Director&y");
        mLocationLabel.setEnabled(enabled);

        // project location entry field
        mLocationPathField = new Text(projectGroup, SWT.BORDER);
        GridData data = new GridData(GridData.FILL_HORIZONTAL);
        data.widthHint = SIZING_TEXT_FIELD_WIDTH;
        mLocationPathField.setLayoutData(data);
        mLocationPathField.setFont(font);
        mLocationPathField.setEnabled(enabled);

        // browse button
        mBrowseButton = new Button(projectGroup, SWT.PUSH);
        mBrowseButton.setFont(font);
        mBrowseButton.setText("B&rowse");
        mBrowseButton.addSelectionListener(new SelectionAdapter()
        {
            public void widgetSelected(SelectionEvent event)
            {
                handleLocationBrowseButtonPressed();
            }
        });

        mBrowseButton.setEnabled(enabled);

        // Set the initial value first before listener
        // to avoid handling an event during the creation.
        if (mInitialLocationFieldValue != null)
            mLocationPathField.setText(mInitialLocationFieldValue.toOSString());
        mLocationPathField.addListener(SWT.Modify, mLocationModifyListener);
    }

    /**
     * @see org.python.pydev.ui.wizards.project.IWizardNewProjectNameAndLocationPage#getLocationPath()
     */
    public IPath getLocationPath()
    {
        if (mUseDefaults)
            return mInitialLocationFieldValue;

        return new Path(getProjectLocationFieldValue());
    }

    /**
     * @see org.python.pydev.ui.wizards.project.IWizardNewProjectNameAndLocationPage#getProjectHandle()
     */
    public IProject getProjectHandle()
    {
        return ResourcesPlugin.getWorkspace().getRoot().getProject(getProjectName());
    }

    /**
     * Returns the current project name as entered by the user, or its
     * anticipated initial value.
     * 
     * @return the project name, its anticipated initial value, or
     *         <code>null</code> if no project name is known
     */
    /* package */String getProjectName()
    {
        if (mProjectNameField == null)
            return mInitialProjectFieldValue;

        return getProjectNameFieldValue();
    }

    /**
     * Returns the value of the project name field with leading and trailing
     * spaces removed.
     * 
     * @return the project name in the field
     */
    private String getProjectNameFieldValue()
    {
        if (mProjectNameField == null)
            return ""; //$NON-NLS-1$
        else
            return mProjectNameField.getText().trim();
    }

    /**
     * Returns the value of the project location field with leading and trailing
     * spaces removed.
     * 
     * @return the project location directory in the field
     */
    private String getProjectLocationFieldValue()
    {
        if (mLocationPathField == null)
            return ""; //$NON-NLS-1$
        else
            return mLocationPathField.getText().trim();
    }

    /**
     * Open an appropriate directory browser
     */
    private void handleLocationBrowseButtonPressed()
    {
        DirectoryDialog dialog = new DirectoryDialog(mLocationPathField.getShell());
        dialog.setMessage("Select the project contents directory.");

        String dirName = getProjectLocationFieldValue();
        if (!dirName.equals("")) { //$NON-NLS-1$
            File path = new File(dirName);
            if (path.exists())
                dialog.setFilterPath(new Path(dirName).toOSString());
        }

        String selectedDirectory = dialog.open();
        if (selectedDirectory != null)
        {
            mCustomLocationFieldValue = selectedDirectory;
            mLocationPathField.setText(mCustomLocationFieldValue);
        }
    }

    /**
     * Returns whether the currently specified project content directory points
     * to an exising project
     */
    private boolean isExistingProjectLocation()
    {
        IPath path = getLocationPath();
        path = path.append(IProjectDescription.DESCRIPTION_FILE_NAME);
        return path.toFile().exists();
    }

    /**
     * Sets the initial project name that this page will use when created. The
     * name is ignored if the createControl(Composite) method has already been
     * called. Leading and trailing spaces in the name are ignored.
     * 
     * @param name initial project name for this page
     */
    /* package */void setInitialProjectName(String name)
    {
        if (name == null)
            mInitialProjectFieldValue = null;
        else
            mInitialProjectFieldValue = name.trim();
    }

    /**
     * Set the location to the default location if we are set to mUseDefaults.
     */
    private void setLocationForSelection()
    {
        if (mUseDefaults)
        {
            IPath defaultPath = Platform.getLocation().append(getProjectNameFieldValue());
            mLocationPathField.setText(defaultPath.toOSString());
        }
    }

    /**
     * Returns whether this page's controls currently all contain valid values.
     * 
     * @return <code>true</code> if all controls are valid, and
     *         <code>false</code> if at least one is invalid
     */
    private boolean validatePage()
    {
        IWorkspace workspace = ResourcesPlugin.getWorkspace();

        String projectFieldContents = getProjectNameFieldValue();
        if (projectFieldContents.equals("")) { //$NON-NLS-1$
            setErrorMessage(null);
            setMessage("Project name is empty");
            return false;
        }

        IStatus nameStatus = workspace.validateName(projectFieldContents, IResource.PROJECT);
        if (!nameStatus.isOK())
        {
            setErrorMessage(nameStatus.getMessage());
            return false;
        }

        String locationFieldContents = getProjectLocationFieldValue();

        if (locationFieldContents.equals("")) { //$NON-NLS-1$
            setErrorMessage(null);
            setMessage("Project location is empty");
            return false;
        }

        IPath path = new Path(""); //$NON-NLS-1$
        if (!path.isValidPath(locationFieldContents))
        {
            setErrorMessage("Project location is not valid");
            return false;
        }

        // commented out. See comments on
        // https://sourceforge.net/tracker/?func=detail&atid=577329&aid=1798364&group_id=85796
        // if (!mUseDefaults
        // && Platform.getLocation().isPrefixOf(
        // new Path(locationFieldContents))) {
        // setErrorMessage("Default location error");
        // return false;
        // }

        if (getProjectHandle().exists())
        {
            setErrorMessage("Project already exists");
            return false;
        }

        if (isExistingProjectLocation())
        {
            setErrorMessage("Project location already exists");
            return false;
        }

        setErrorMessage(null);
        setMessage(null);
        return true;
    }

    /*
     * see @DialogPage.setVisible(boolean)
     */
    public void setVisible(boolean visible)
    {
        super.setVisible(visible);
        if (visible)
            mProjectNameField.setFocus();
    }

    /**
     * @see org.python.pydev.ui.wizards.project.IWizardNewProjectNameAndLocationPage#shouldCreatSourceFolder()
     */
    public boolean shouldCreatSourceFolder()
    {
        return true;
    }
}
